package cn.sat.miniodemo.template;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.sat.miniodemo.config.OssProperties;
import cn.sat.miniodemo.entity.OssFile;
import io.micrometer.common.util.StringUtils;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.*;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import java.io.InputStream;
import java.time.ZonedDateTime;
import java.util.*;

/**
 * MinIO 模板类
 */
@Slf4j
@AllArgsConstructor
public class MinioTemplate {
    /**
     * MinIO 客户端
     */
    MinioClient minioClient;
    /**
     * MinIO 配置类
     */
    OssProperties ossProperties;

    @SneakyThrows
    public List<Bucket> listBuckets() {
        return minioClient.listBuckets();
    }

    @SneakyThrows
    public boolean bucketExists(String bucketName) {
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    @SneakyThrows
    public void makeBucket(String bucketName) {
        if (!bucketExists(bucketName)) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    @SneakyThrows
    public void removeBucket(String bucketName) {
        minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
    }

    @SneakyThrows
    public List<OssFile> putObjects(List<InputStream> inputStreams, String bucketName, List<String> originalFileNames) {
        List<OssFile> results = new ArrayList<>();

        if (StringUtils.isEmpty(bucketName)) {
            bucketName = ossProperties.getDefaultBucketName();
        }

        String uuid = UUID.randomUUID().toString();
        for (int i = 0; i < inputStreams.size(); i++) {
            InputStream inputStream = inputStreams.get(i);
            String originalFileName = originalFileNames.get(i);
            String uuidFileName = generateOssUuidFileName(originalFileName, uuid);

            try {
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(uuidFileName)
                        .stream(inputStream, inputStream.available(), -1)
                        .build());
                results.add(new OssFile(uuidFileName, originalFileName));
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
            }
        }

        return results;
    }


    @SneakyThrows
    public OssFile putObject(InputStream inputStream, String bucketName, String originalFileName) {
        String uuid = UUID.randomUUID().toString();
        String uuidFileName = generateOssUuidFileName(originalFileName, uuid);
        try {
            if (StringUtils.isEmpty(bucketName)) {
                bucketName = ossProperties.getDefaultBucketName();
            }
            ObjectWriteResponse data = minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(uuidFileName)
                    .stream(inputStream, inputStream.available(), -1)
                    .build());
            log.info("上传成功：{}", data.object());
            return new OssFile(data.object(), originalFileName);
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    /**
     * 获取文件预览链接
     * @param bucketName 存储桶名称
     * @param ossFilePath 文件路径
     * @return 预览链接
     */
    @SneakyThrows
    public String getPresignedObjectUrl(String bucketName, String ossFilePath) {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(Method.GET)
                .bucket(bucketName)
                .object(ossFilePath)
                .expiry(60 * 60 * 24)
                .build());
    }

    /**
     * 获取文件
     * @param bucketName 存储桶名称
     * @param ossFilePath 文件路径
     * @return 文件输入流
     */
    @SneakyThrows
    public InputStream getObject(String bucketName, String ossFilePath) {
        return minioClient.getObject(GetObjectArgs.builder()
                .bucket(bucketName)
                .object(ossFilePath)
                .build());
    }

    /**
     * 获取文件列表
     * @param bucketName 存储桶名称
     * @param recursive 是否递归
     * @return 文件列表
     */
    @SneakyThrows
    public Iterable<Result<Item>> listObjects(String bucketName, boolean recursive) {
        return minioClient.listObjects(ListObjectsArgs.builder()
                .bucket(bucketName)
                .recursive(recursive)
                .build());
    }

    @SneakyThrows
    public Map<String, String> getPresignedPostFormData(String bucketName, String fileName) {
        PostPolicy policy = new PostPolicy(bucketName, ZonedDateTime.now().plusDays(7));
        policy.addEqualsCondition("key", fileName);
        policy.addStartsWithCondition("Content-Type", "image/");
        policy.addContentLengthRangeCondition(64 * 1024, 10 * 1024 * 1024);
        return minioClient.getPresignedPostFormData(policy);
    }

    @SneakyThrows
    public void initDefaultBucket() {
        String defaultBucketName = ossProperties.getDefaultBucketName();
        if (bucketExists(defaultBucketName)) {
            log.info("默认存储桶已存在");
        } else {
            log.info("创建默认存储桶");
            makeBucket(ossProperties.getDefaultBucketName());
        }
    }
    @SneakyThrows
    public void initExportBucket() {
        String exportBucketName = ossProperties.getExportBucketName();
        if (bucketExists(exportBucketName)) {
            log.info("导出存储桶已存在");
        } else {
            log.info("导出默认存储桶");
            makeBucket(ossProperties.getExportBucketName());
            LifecycleConfiguration config = getLifecycleConfiguration(7);

            minioClient.setBucketLifecycle(
                    SetBucketLifecycleArgs.builder()
                            .bucket(exportBucketName)
                            .config(config)
                            .build());
        }
    }

    /**
     * 生命周期规则
     * @param days 过期天数
     * @return 生命周期规则
     */
    private static @NotNull LifecycleConfiguration getLifecycleConfiguration(Integer days) {
        LifecycleRule rule = new LifecycleRule(
                Status.ENABLED,                                   // 规则状态
                null,                                             // 中断未完成上传
                new Expiration((ResponseDate) null, days, null),    // 7 天后过期
                new RuleFilter(""),                               // 匹配所有对象
                null,                                             // 规则ID（自动生成）
                null,                                             // 非当前版本过期
                null,                                             // 非当前版本转换
                null                                              // 转换规则
        );

        return new LifecycleConfiguration(List.of(rule));
    }

    public String generateOssUuidFileName(String originalFilename, String id) {
        id = StringUtils.isEmpty(id) ? UUID.randomUUID().toString() : id;
        return "files" + StrUtil.SLASH + DateUtil.format(new Date(), "yyyy-MM-dd") + StrUtil.SLASH + id + StrUtil.SLASH + originalFilename;
    }

}